from common.CommonNode import CommonNode
from common.CommonHeap import CommonHeap
from common.Utils import Utils


class Node(CommonNode):
    def __init__(self, size, start, obj):
        super().__init__(size, start, obj)
        self.marked = False
        self.forwarding = None

    def setChildren(self, children):
        self.children = children

    def setForwarding(self, forwarding):
        self.forwarding = forwarding

    def getForwarding(self):
        return self.forwarding

    def mark(self):
        self.marked = True

    def unMark(self):
        self.marked = False

    def isMarked(self):
        return self.marked


class Heap:
    def __init__(self, size):
        self.fromHeap = Node(size / 2, 0, None)
        self.toHeap = Node(size / 2, 0, None)
        self.roots = []
        self.frm = self.fromHeap
        self.to = self.toHeap
        self.size = size
        self.usedSize = 0
        self.chunk = 1
        self.toChunk = 1
        self.toUsedSize = 0

    def __str__(self):
        return f"total: {self.size / 2}, used: {self.usedSize}, chunk: {self.chunk}"

    def addToRoot(self, node):
        self.roots.append(node)

    def removeFromRoot(self, node):
        Utils.popIfExist(self.roots, node)

    @staticmethod
    def reference(nodeA, nodeB):
        nodeA.addChild(nodeB)

    @staticmethod
    def unReference(nodeA, nodeB):
        nodeA.removeChild(nodeB)

    def newObject(self, size, obj):
        nd, addChunk = CommonHeap.firstFit(self.frm, size, Node)
        if nd is None:
            self.gc()
            nd, addChunk = CommonHeap.firstFit(self.frm, size, Node)
            if nd is None:
                raise Exception("分配失败")
        self.usedSize += size
        if addChunk:
            self.chunk += 1
        nd.setObj(obj)
        return nd

    def freeObject(self, node):
        decChunk = CommonHeap.freeObject(node)
        node.setObj(None)
        self.chunk -= decChunk
        self.usedSize -= node.getSize()

    def mark(self):
        CommonHeap.mark(self.roots)

    def copy2To(self, p):
        nd, addChunk = CommonHeap.firstFit(self.to, p.getSize(), Node)
        nd.setObj(p.getObj())
        nd.setChildren(p.getChildren())
        if addChunk:
            self.toChunk += 1
        self.toUsedSize += p.getSize()
        return nd

    def copy(self):
        p = self.frm
        while p is not None:
            if p.isMarked():
                nd = self.copy2To(p)
                p.setForwarding(nd)
            p = p.getNext()

    def resetAddrAndFree(self):
        # 更新root
        newRoots = []
        for r in self.roots:
            newRoots.append(r.getForwarding())
        self.roots = newRoots

        # 更新地址
        p = self.to
        while not p.isFree():
            newAddr = []
            for cld in p.getChildren():
                newAddr.append(cld.getForwarding())
            p.setChildren(newAddr)
            p = p.getNext()

        # 清楚from空间
        p = self.frm
        while p.getNext() is not None:
            CommonNode.merge(p, p.getNext())
        p.setObj(None)

        # 交换空间
        t = self.frm
        self.frm = self.to
        self.to = t

        # 重置参数
        self.chunk = self.toChunk
        self.toChunk = 0
        self.usedSize = self.toUsedSize
        self.toUsedSize = 0

    def gc(self):
        self.mark()
        self.copy()
        self.resetAddrAndFree()


if __name__ == '__main__':
    heap = Heap(1024)
    n0 = heap.newObject(10, "1024")
    n1 = heap.newObject(12, "addc")
    heap.addToRoot(n0)
    heap.reference(n0, n1)
    heap.gc()
    print(heap)
